Механизм репликации FreeIPA
===========================

Репликация — это процесс, в ходе которого содержимое каталога синхронизируется между серверами, за счет чего достигается целостность данных или так называемая конвергентность. В службе каталога **FreeIPA** используется протокол «DS 5.0 multi-supplier incremental replication protocol», в котором учитываются отдельные положения (**RFC3384**).

Каждая Реплика FreeIPA является Мастером, т.е. доступна на запись, и с ее помощью можно вносить изменения в каталог. Разрешение конфликтов основано на использовании меток времени, поэтому для корректной работы механизма крайне важно, чтобы время было синхронизировано между всеми серверами и ни в коем случае не уходило вперед/назад более, чем на сутки, иначе для устранения проблемы искажения времени (too much time skew) может потребоваться восстановления контроллеров из резервной копии.

Соглашения о репликации
-----------------------

На низком уровне репликация проходит по модели **Ведущий-Ведомый** (**Master-Slave**), в рамках которой Сервер, передающий изменения, называется **Поставщиком**, а Сервер, принимающий изменения, называется **Потребителем**. По итогам репликации на **Потребителе** формируется полная копия данных каталога, поэтому его также называют **Репликой**.

Для работы механизма репликации требуется заранее определить топологию домена через создание **Сегментов** и **Соглашений о репликации**. **Сегмент топологии** (**Topology segment**) определяет связь между двумя узлами и является информацией, которая реплицируется в домене между всеми контроллерами, а **Соглашения о репликации** хранятся только на **Поставщике** и содержат настройки для подключения к **Потребителю**. Таким образом, на каждый двусторонний **Сегмент** приходится по два **Соглашения**.

Для управления топологией на портале управления нужно перейти к странице **Управление доменом** → **Сайты и службы** → **Соглашения о репликации**, см. :ref:`рис. 2.4`.

.. figure:: media/10_replication-agreements.png
   :name: рис. 2.4
   :scale: 50

   Соглашения о репликации контроллера домена **dc-1** с резервным контроллером **dc-2**

В интерфейсе сущности названы **Соглашениями**, но, по сути, это **Сегменты топологии**. Соответствующие **Соглашения о репликации** будут созданы на каждом из контроллеров домена автоматически, как только ими будет получена информация о соответствующем **Сегменте топологии**, за что отвечает плагин топологии (IPA Topology Configuration).

Порядок решения конфликтов репликации
-------------------------------------

Репликация всегда инициируется **Поставщиком**, а не **Потребителем** (т.е. работает по модели **Push**). Алгоритм основан на использовании журнала изменений, который хранится в отдельной базе данных, для ALSE до версии **1.7.4** см. файл «/var/lib/dirsrv/slapd-ALD-COMPANY-LOCAL/cldb/\*.db», для **ALSE 1.7.4** и выше см. файл */var/lib/dirsrv/slapd-ALD-COMPANY-LOCAL/db/userRoot/replication_changelog.db*.

Открыть файл можно командой **dbscan -f**:

.. code-block:: bash
        
   dbscan -f
   /var/lib/dirsrv/slapd-ALD-COMPANY-LOCAL/db/userRoot/replication_change
   log.db

В сеансе репликации **Поставщик** связывается с **Потребителем**, запрашивает у того сводную информацию о состоянии его репликации (так называемую таблицу RUV, Replica Update Vector), чтобы определить, есть ли у него для **Потребителя** более свежие данные, которые можно было бы передать, и запускает передачу данных. В случае изменения одного и того же атрибута на двух разных контроллерах домена, при репликации приоритет будет выше у последнего изменения по времени.

В случае, когда на нескольких контроллерах одновременно создаются записи с одинаковыми DN, автоматический механизм разрешения конфликтов оставит только ту запись, которая была создана раньше других по метке времени. Остальные записи будут переименованы и скрыты. К имени записи добавляется атрибут ``nsuniqueid``, например, дубликат учетной записи пользователя **ivan.kuznetsov** будет называться ``nsuniqueid=7341f021-20b331ee-a3ef96ae-a91d3705+uid=ivan.kuznetsov,cn=accounts,dc=ald,dc=company,dc=lan``.

Дополнительно таким записям назначается атрибут ``nsds5ReplConflict``, чтобы их было легко найти в каталоге:

.. code-block:: bash

   ldapsearch -H ldap://localhost:389 -x -D "cn=Directory Manager" -W -b "cn=users,cn=accounts,dc=ald,dc=company,dc=local" -s one -a always -z 1000 "(|(objectClass=subentry)(objectClass=ldapSubentry))" "hasSubordinates" "objectClass"

Если создать пользователя на одном сервере, а на другом сервере удалить структурное подразделение, в котором этот пользователь был создан, то на уровне репликации LDAP конфликт не произойдёт, т.к. отношение пользователя к структурному подразделению в ALD Pro реализуется через атрибут ``rbtadp``, а не через связь родительской и дочерней записи. Пользователь не будет отражаться в каком-либо структурном подразделении. Но будет присутствовать в списке всех пользователей.

Проверка статуса репликации
------------------------------------------------------------------------------

Проверка статуса репликации утилитой dsconf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Чтобы получить детальную информацию по соглашениям о репликации текущего сервера, нужно выполнить следующую команду:

.. code-block:: console

    sudo dsconf -j ALD-COMPANY-LAN replication status --suffix dc=ald,dc=company,dc=lan

Информация будет предоставлена в json-формате:

.. code-block:: json

    {
        "type": "list", "items": [{
            "agmt-name": ["meTodc-2.ald.company.lan"],
            "replica": ["dc-2.ald.company.lan:389"],
            "replica-enabled": ["on"],
            "update-in-progress": ["FALSE"],
            "last-update-start": ["20230824143651Z"],
            "last-update-end": ["20230824143651Z"],
            "number-changes-sent": ["4:25/723 5:1/0 "],
            "number-changes-skipped": ["unavailable"],
            "last-update-status": ["Error (0) Replica acquired successfully: Incremental update succeeded"],
            "last-init-start": ["20230824120948Z"],
            "last-init-end": ["20230824120954Z"],
            "last-init-status": ["Error (0) Total update succeeded"],
            "reap-active": ["0"],
            "replication-status": ["Not in Synchronization: supplier (64e76843000000040000) consumer (Unavailable) State (green) Reason (error (0) replica acquired successfully: incremental update succeeded)"],
            "replication-lag-time": ["unavailable"]
        }]
    }

Полученную информацию можно представить в формате таблицы, но потребуется установить пакет ``jq`` и использовать комплексную команду форматирования, которую можно сохранить в виде скрипта:

.. code-block:: console

    sudo apt install jq -y

    (printf 'SUFFIX \tAGREEMENT \tSTATE \tTIME-SINCE \tLDAP-STATUS \tREPL-STATUS \n'; sudo dsconf -j ALD-COMPANY-LAN replication list | jq '.items[]' -r | xargs -P8 -i -- sudo dsconf -j ALD-COMPANY-LAN repl-agmt list --suffix={} | jq '.items[].attrs | (.nsds5replicalastupdatestatusjson[0] | fromjson) as $status | [.nsds5replicaroot[0], .cn[0], $status.state, $status.date, $status.ldap_rc_text, $status.repl_rc_text] | @tsv' -r | sort ) | column -s$'\t' -t

Результат выполнения команды смотри на рисунке :ref:`replication-agreements`.

.. figure:: media/13_replication-agreements-table.png
    :name: replication-agreements
    :scale: 50

    Состояние соглашений о репликации в табличном виде

Есть также возможность с помощью команды ``replication monitor`` утилиты ``dsconf`` собрать информацию сразу со всех контроллеров домена, но в состоянии репликации не будет указан агрегированный статус, что затруднит анализ:

.. code-block:: console

    sudo dsconf ldap://localhost:389 replication monitor

Для каждого сервера в домене нужно будет ввести учетные данные пользователя ``cn=Directory Manager``:

.. code-block:: console
    
    Enter Bind DN: cn=Directory Manager
    Enter password for cn=Directory Manager on ldap://localhost:389: *****
    
    Enter a bind DN for dc-2.ald.company.lan:389: cn=Directory Manager
    Enter a password for cn=Directory Manager on dc-2.ald.company.lan:389: *****

Чтобы не вводить учетные данные каждый раз вручную, на рабочей станции администратора их можно определить в текстовом файле ``~/.dsrc``:

.. code-block:: console

    [repl-monitor-connections]
    connection1 = dc-1.ald.company.lan:389:cn=Directory Manager:*
    connection2 = dc-2.ald.company.lan:389:cn=Directory Manager:[~/pwd.txt]
    connection3 = dc-3.ald.company.lan:389:cn=Directory Manager:S3cret

Где:

* для dc-1 будет запрошен пароль;
* для dc-2 пароль будет взят из файла ``pwd.txt``;
* для dc-3 в качестве пароля будет использована строка ``S2cret``.

Проверка репликации с помощью скриптов checkipaconsistency
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. attention::

    В данном разделе представлена информация о том, как можно проверить целостность домена с использованием модуля **checkipaconsistency** из публичного pip-репозитория, установка которого в продуктивных средах без дополнительной проверки скриптов категорически запрещается.

Сначала проверьте текущую версию Python, в ALSE 1.7.ххх по умолчанию устанавливается 3.7.3:

.. code-block:: console

    sudo python3 -V
    Python 3.7.3

Скачайте установочный файл системы управления пакетами ``pip`` соответствующей версии:

.. code-block:: console

    wget https://bootstrap.pypa.io/pip/3.7/get-pip.py

    --2024-11-25 10:44:34--  https://bootstrap.pypa.io/pip/3.7/get-pip.py
    Распознаётся bootstrap.pypa.io (bootstrap.pypa.io)… 151.101.84.175, 2a04:4e42:14::175
    Подключение к bootstrap.pypa.io (bootstrap.pypa.io)|151.101.84.175|:443... соединение установлено.
    HTTP-запрос отправлен. Ожидание ответа… 200 OK
    Длина: 2636033 (2,5M) [text/x-python]
    Сохранение в: «get-pip.py»

    get-pip.py                                     100%[=================>]   2,51M  5,32MB/s    за 0,5s    

    2024-11-25 10:44:35 (5,32 MB/s) - «get-pip.py» сохранён [2636033/2636033]

Установите ``pip``:

.. code-block:: console

    sudo python3 ./get-pip.py 

    Collecting pip<24.1
    Downloading pip-24.0-py3-none-any.whl.metadata (3.6 kB)
    Collecting wheel
    Downloading wheel-0.42.0-py3-none-any.whl.metadata (2.2 kB)
    Downloading pip-24.0-py3-none-any.whl (2.1 MB)
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 4.4 MB/s eta 0:00:00
    Downloading wheel-0.42.0-py3-none-any.whl (65 kB)
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 65.4/65.4 kB 1.3 MB/s eta 0:00:00
    DEPRECATION: python-apt 1.8.4.3-ci202309111239-astra1-b1 has a non-standard version number. pip 24.1 will enforce this behaviour change. A possible replacement is to upgrade to a newer version of python-apt or contact the author to suggest that they release a version with a conforming version number. Discussion can be found at https://github.com/pypa/pip/issues/12063
    Installing collected packages: wheel, pip
    Successfully installed pip-24.0 wheel-0.42.0
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

Установите модуль ``checkipaconsistency``:

.. code-block:: console
    
    sudo python3 -m pip install checkipaconsistency

    Collecting checkipaconsistency
    Downloading checkipaconsistency-2.7.10-py2.py3-none-any.whl.metadata (9.6 kB)
    Collecting pplogger (from checkipaconsistency)
    Downloading pplogger-4.2.0-py2.py3-none-any.whl.metadata (1.4 kB)
    Requirement already satisfied: python-ldap in /usr/lib/python3/dist-packages (from checkipaconsistency) (3.1.0)
    Collecting prettytable (from checkipaconsistency)
    Downloading prettytable-3.7.0-py3-none-any.whl.metadata (26 kB)
    Requirement already satisfied: dnspython in /usr/lib/python3/dist-packages (from checkipaconsistency) (1.16.0)
    Collecting importlib-metadata (from prettytable→checkipaconsistency)
    Downloading importlib_metadata-6.7.0-py3-none-any.whl.metadata (4.9 kB)
    Collecting wcwidth (from prettytable→checkipaconsistency)
    Downloading wcwidth-0.2.13-py2.py3-none-any.whl.metadata (14 kB)
    Collecting zipp>=0.5 (from importlib-metadata→prettytable→checkipaconsistency)
    Downloading zipp-3.15.0-py3-none-any.whl.metadata (3.7 kB)
    Collecting typing-extensions>=3.6.4 (from importlib-metadata→prettytable→checkipaconsistency)
    Downloading typing_extensions-4.7.1-py3-none-any.whl.metadata (3.1 kB)
    Downloading checkipaconsistency-2.7.10-py2.py3-none-any.whl (24 kB)
    Downloading pplogger-4.2.0-py2.py3-none-any.whl (14 kB)
    Downloading prettytable-3.7.0-py3-none-any.whl (27 kB)
    Downloading importlib_metadata-6.7.0-py3-none-any.whl (22 kB)
    Downloading wcwidth-0.2.13-py2.py3-none-any.whl (34 kB)
    Downloading typing_extensions-4.7.1-py3-none-any.whl (33 kB)
    Downloading zipp-3.15.0-py3-none-any.whl (6.8 kB)
    DEPRECATION: python-apt 1.8.4.3-ci202309111239-astra1-b1 has a non-standard version number. pip 24.1 will enforce this behaviour change. A possible replacement is to upgrade to a newer version of python-apt or contact the author to suggest that they release a version with a conforming version number. Discussion can be found at https://github.com/pypa/pip/issues/12063
    Installing collected packages: wcwidth, pplogger, zipp, typing-extensions, importlib-metadata, prettytable, checkipaconsistency
    Successfully installed checkipaconsistency-2.7.10 importlib-metadata-6.7.0 pplogger-4.2.0 prettytable-3.7.0 typing-extensions-4.7.1 wcwidth-0.2.13 zipp-3.15.0
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

При установке модуля в системе будет создан скрипт ``/usr/local/bin/cipa``, который позволит запускать утилиту в одну строку. Учитывая, что пароль нужно передавать утилите открытым текстом, не забудьте поставить дополнительный пробел перед именем утилиты cipa или отключить ведение истории:

.. code-block:: console
    
    set +o history
    cipa -d ald.company.lan -W 'AstraLinux_176'

    +--------------------+---------+---------+-------+
    | FreeIPA servers:   | dc-1    | dc-2    | STATE |
    +--------------------+---------+---------+-------+
    | Active Users       | 1       | 1       | OK    |
    | Stage Users        | 0       | 0       | OK    |
    | Preserved Users    | 0       | 0       | OK    |
    | Hosts              | 2       | 2       | OK    |
    | Services           | 10      | 10      | OK    |
    | User Groups        | 6       | 6       | OK    |
    | Host Groups        | 1       | 1       | OK    |
    | Netgroups          | 0       | 0       | OK    |
    | HBAC Rules         | 2       | 2       | OK    |
    | SUDO Rules         | 0       | 0       | OK    |
    | DNS Zones          | 2       | 2       | OK    |
    | Certificates       | 0       | 0       | OK    |
    | LDAP Conflicts     | 0       | 0       | OK    |
    | Ghost Replicas     | 0       | 0       | OK    |
    | Anonymous BIND     | ROOTDSE | ROOTDSE | OK    |
    | Microsoft ADTrust  | True    | True    | OK    |
    | Replication Status | dc-2 0  | dc-1 0  | OK    |
    +--------------------+---------+---------+-------+
